#include "../../core/pch.h"
#include "../../pi/binarysemaphore.h"
#include "../../pi/process.h"
#include "../utils/utils.h"

#include "ipgiver.h"
#include "receiver.h"
#include "sender.h"
#include "logger.h"

/** Shared memory specific variables. */
int segment_id, segment_size, sem_id;
struct info_table* shared_memory;
struct shmid_ds shm_buffer;

/** How much seconds to wait before receiver starts working. */
int receiver_delay;

/** File pointers. */
FILE* primes_file;
FILE* out_file;

int logger_pid, sender_pid, receiver_pid, ipgiver_pid;

struct option long_options[] =
{ 
	{"number", required_argument, 0, 'a'},
	{"delay", required_argument, 0, 'b'},
	{"recvport", required_argument, 0, 'c'},
	{"logport", required_argument, 0, 'd'},
	{"ipgport", required_argument, 0, 'e'},
	{0, 0, 0, 0}
};

int main(int argc, char* argv[])
{
	/** Ports. */
	int logger_port;
	int receiver_port;
	int ipg_port;

	/** Number to factor. */
	unsigned long long number;

	/** Open files. */
	out_file 	= fopen(RESULTS_FILE, "w+");
	primes_file = fopen(PRIMES_FILE, "r+b");

	PF_clear_primes(primes_file);

	if (argc < 9)
	{
		DEBUGS("Usage: init --number <num> --logport <port> --recvport <port> --ipgport <port> [--delay <sec>]");
		exit(STATUS_ERR);
	}

	while (true)
	{
		int option_index = 0;
		int op = getopt_long(argc, argv, "a:b:c:d:e:", long_options, &option_index);
		if (op == NO_MORE_OPTIONS)
			break;

		switch (op)
		{
			case 'a':
				number = strtoull(optarg, NULL, 10);
				break;
			case 'b':
				receiver_delay = atoi(optarg);
				break;
			case 'c':
				receiver_port = atoi(optarg);
				break;
			case 'd':
				logger_port = atoi(optarg);
				break;
			case 'e':
				ipg_port = atoi(optarg);
				break;
			default:
				exit(STATUS_ERR);
		}
	}

	if (isPrime(number))
	{
		write_result(out_file, number, number, 1);
		fclose(out_file);
		exit(STATUS_OK);
	}

	/* Allocate shared memory segment. */
	segment_id = shmget(IPC_PRIVATE, sizeof(struct info_table), 
		IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);

	/* Attach shared memory segment. */
	shared_memory = (struct info_table*) shmat(segment_id, NULL, 0);
	memset(shared_memory, 0, sizeof(struct info_table));
	shared_memory->n = number;

	/* Allocate and init semaphore for this memory. */
	BinarySemaphore::Create(sem_id);
	BinarySemaphore::Init(sem_id);

	/* Run ip process which gives ip. */
	if (Process::Create() == 0)
	{
		IpGiver ipGiver(ipg_port);
		ipGiver.InitNetwork();
		ipGiver.Listen();
	}

	/* Start logging process. */
	if ((logger_pid = Process::Create()) == 0)
		logger(segment_id, sem_id, logger_port, shared_memory,
			primes_file);

	sleep(receiver_delay);

	/* Start receiver process. */
	if ((receiver_pid = Process::Create()) == 0)
		receiver(segment_id, sem_id, receiver_port, shared_memory,
			out_file, primes_file, sender_pid, logger_pid);

	/* Start sender process. */
	if ((sender_pid = Process::Create()) == 0)
		sender(segment_id, sem_id, shared_memory, primes_file,
			receiver_pid, logger_pid);
	
	DEBUGS("sender pid = %d\n", sender_pid);

	/** Wait for children. */
	Process::Wait();
	Process::Wait();
	Process::Wait();
	Process::Wait();

	DEBUGS("init: all children exited");

	/**
	 * Destroy semaphore alloceted for processes
	 * synchronization.
	 */
	BinarySemaphore::Destroy(sem_id);

	/**
	 * Detach shared memory segment and 
	 * deallocate shared memory.
	 */
	shmdt(shared_memory);
	shmctl(segment_id, IPC_RMID, NULL);

	/** Close files. */
	fclose(out_file);
	fclose(primes_file);

	return 0;
}
